/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package avl.sv.shared.XML;

import avl.sv.shared.Annotations.AnnotationSet;
import avl.sv.shared.SlideReference;
import avl.sv.shared.Solution;
import avl.sv.shared.ClassifierClass;
import avl.sv.shared.model.classifier.AbstractClassifier;
import avl.sv.shared.model.classifier.ClassifierSVM;
import avl.sv.shared.model.featureGenerator.AbstractFeatureGenerator;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.DatatypeConverter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import libsvm.svm_parameter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 *
 * @author benbryan
 */
public class SolutionXML_Writer {
    
    public static String getXMLString(Solution solution) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
      
        try {
            DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            // root elements
            Document doc = docBuilder.newDocument();
            Element rootElement = doc.createElement("Solution");
            rootElement.setAttribute("TileDim", String.valueOf(solution.tileDim));
            rootElement.setAttribute("Description", String.valueOf(solution.description));
            doc.appendChild(rootElement);
            
            Iterator<AbstractClassifier> solutionIter = solution.getClassifierIterator();
            while(solutionIter.hasNext()){
                AbstractClassifier classifier = solutionIter.next();
                if (classifier instanceof ClassifierSVM) {
                    ClassifierSVM classifierSVM = (ClassifierSVM) classifier;
                    Element classifierNode = doc.createElement("ClassifierSVM");
                    rootElement.appendChild(classifierNode);

                    Element modelNode = doc.createElement("Model");
                    classifierNode.appendChild(modelNode);
                    modelNode.setTextContent(classifierSVM.svmModelToString());

                    Element zscoreNode = doc.createElement("Zscore");
                    classifierNode.appendChild(zscoreNode);
                    zscoreNode.setTextContent(classifierSVM.zscoreToString());

                    Element classNamesNode = doc.createElement("ClassNames");
                    classifierNode.appendChild(classNamesNode);
                    String[] names = classifierSVM.classNames;
                    if (names != null) {
                        for (int i = 0; i < names.length; i++) {
                            classNamesNode.setAttribute("c" + String.valueOf(i), names[i]);
                        }
                    }

                    Element paramNode = doc.createElement("Param");
                    classifierNode.appendChild(paramNode);
                    svm_parameter p = classifierSVM.param;
                    paramNode.setAttribute("svm_type", String.valueOf(p.svm_type));
                    paramNode.setAttribute("kernel_type", String.valueOf(p.kernel_type));
                    paramNode.setAttribute("degree", String.valueOf(p.degree));
                    paramNode.setAttribute("gamma", String.valueOf(p.gamma));
                    paramNode.setAttribute("coef0", String.valueOf(p.coef0));
                    paramNode.setAttribute("nu", String.valueOf(p.nu));
                    paramNode.setAttribute("cache_size", String.valueOf(p.cache_size));
                    paramNode.setAttribute("C", String.valueOf(p.C));
                    paramNode.setAttribute("eps", String.valueOf(p.eps));
                    paramNode.setAttribute("p", String.valueOf(p.p));
                    paramNode.setAttribute("shrinking", String.valueOf(p.shrinking));
                    paramNode.setAttribute("probability", String.valueOf(p.probability));
                    paramNode.setAttribute("nr_weight", String.valueOf(p.nr_weight));
                    paramNode.setAttribute("autoGamma", String.valueOf(classifierSVM.autoGamma));
                }
            }
            
            Iterator<AbstractFeatureGenerator> featureIter = solution.getFeatureIterator();
            while(featureIter.hasNext()){
                AbstractFeatureGenerator featureGenerator = featureIter.next();
                Element featuresNode = doc.createElement("FeatureGenerator");
                rootElement.appendChild(featuresNode);
                String[] featureNames = featureGenerator.getFeatureNames();
                StringBuilder sb = new StringBuilder();
                for (String featureName:featureNames){
                    sb.append(featureName).append(",");
                }
                featuresNode.setAttribute("generatorName",featureGenerator.getClass().getName());
                featuresNode.setAttribute("featureNames", sb.toString());
            }
            for (ClassifierClass c : solution.getClasses()) {
                appendClassifierClass(doc, c, rootElement);
            }

            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            javax.xml.transform.Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);

            // Output to console for testing
            StreamResult result = new StreamResult(bos);
            transformer.transform(source, result);

        } catch (TransformerException | ParserConfigurationException ex) {
            Logger.getLogger(SolutionXML_Writer.class.getName()).log(Level.SEVERE, null, ex);
        } 
        
        return bos.toString();
    }

    private static void appendClassifierClass(Document doc, ClassifierClass c, Element rootNode) {
        Element classifierClassNode = doc.createElement("ClassifierClass");
        rootNode.appendChild(classifierClassNode);
        classifierClassNode.setAttribute("Name", c.getClassName());       
        for (AnnotationSet annoSet : c.getAnnotationSets()) {
            appendAnnotations(doc, annoSet, classifierClassNode);
        }
    }

    private static void appendAnnotations(Document doc, AnnotationSet annos, Element classifierClassNode) {
        Element annotationsNode = doc.createElement("Annotations");
        classifierClassNode.appendChild(annotationsNode);
        SlideReference slideReference = annos.getSlideReference();
        appendSlideNode(doc, annotationsNode, slideReference);
        AnnotationXML_Writer.appendAnnotations(doc, annos.getAnnotations(), annotationsNode);
    }
    
    protected static void appendSlideNode(Document doc, Element slideSetNode, SlideReference slideRef) {       
        Element slideNode = doc.createElement("slide");
        slideNode.setAttribute("slideName", slideRef.slideName);
        slideNode.setAttribute("slideSetName", slideRef.slideSetName);
        slideNode.setAttribute("hash", DatatypeConverter.printBase64Binary(slideRef.hash));
        slideSetNode.appendChild(slideNode);
    }
    
}
